/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */
package info.peigen.hotpot.common.service.base;


import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import info.peigen.hotpot.common.core.result.ListResult;
import info.peigen.hotpot.common.core.result.PageResult;
import info.peigen.hotpot.common.core.result.SingleResult;
import info.peigen.hotpot.common.service.base.event.ServiceOrderLogEvent;
import info.peigen.hotpot.common.service.base.event.ServiceResultLogEvent;
import info.peigen.hotpot.common.service.base.gateway.GatewayFacade;
import info.peigen.hotpot.common.service.base.test.mock.ServiceTestMockContext;
import info.peigen.hotpot.common.service.base.test.mock.ServiceTestMockContextThreadLocal;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.noear.solon.Solon;
import org.noear.solon.SolonProps;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.AppContext;
import org.noear.solon.core.event.EventBus;
import org.noear.solon.data.cache.CacheService;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <b>(AbstractService)</b>
 * 所有服务层的抽象类,提供最基础的流程支撑
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2019/05/19
 */
@SuppressWarnings({"rawtypes", "resource", "Convert2MethodRef"})
@Slf4j
@FieldDefaults(level = AccessLevel.PROTECTED)
public abstract class AbstractService implements ServiceFacade {

    @Inject
    AppContext   context;
    @Inject
    CacheService cacheService;

    private SolonProps cfg() {
        return Solon.cfg();
    }

    /**
     * 领域模型的命令模式执行器
     *
     * @param serviceContext ServiceContext
     * @param invoke         ServiceInvoke
     */
    public void execute(ServiceContext serviceContext, ServiceInvoke invoke) {

        // 0. 打印[开始]日志
        StopWatch watch = serviceContext.getWatch();
        watch.start("入参打印");
        ServiceOrderBase     order    = serviceContext.getOrder();
        ServiceOrderLogEvent logEvent = ServiceOrderLogEvent.builder().order(order).build();
        eventPost(logEvent);
        watch.stop();
        try {
            // 1. 校验入参
            watch.start("入参校验");
            invoke.checkOrder(serviceContext);
            watch.stop();

            // 2. request组装
            watch.start("组装请求");
            invoke.installRequest(serviceContext);
            watch.stop();
            //
            // // 3. 数据初始化
            // watch.start("数据初始化");
            // invoke.handleInitDataEvent(serviceContext);
            // watch.stop();

            // 4. 服务调用
            if (cfg().isDebugMode()) {
                watch.start("Mock封装");
                if (ObjectUtil.isNull(ServiceTestMockContextThreadLocal.currentContext())) {
                    new ServiceTestMockContextThreadLocal(ServiceTestMockContext.builder().build());
                }

                // 4.1 Mock Gateway Service Facade
                if (ObjectUtil.isNotNull(serviceContext.getGatewayFacade())) {
                    List<GatewayFacade> beans       = context.getBeansOfType(GatewayFacade.class);
                    String              serviceName = serviceContext.getGatewayFacade().getClass().getSimpleName();
                    beans.stream().filter(bean -> StrUtil.equals(bean.getClass().getSimpleName(), "Mock"))
                            .filter(bean -> bean.getClass().getSimpleName().replaceAll("Mock", "")
                                    .equalsIgnoreCase(serviceName))
                            .findAny()
                            .ifPresent(mockGatewayFacade -> serviceContext.setGatewayFacade(mockGatewayFacade));
                }
                // 4.2 Mock Domain Service
                if (ObjectUtil.isNotNull(serviceContext.getDomainService())) {
                    List<DomainService> domainServiceBeans = context.getBeansOfType(DomainService.class);
                    String domainServiceName = serviceContext.getDomainService().getClass()
                            .getSimpleName();

                    domainServiceBeans.stream().filter(bean -> StrUtil.equals(bean.getClass().getSimpleName(), "Mock"))
                            .filter(bean -> bean.getClass().getSimpleName().replaceAll("Mock", "")
                                    .equalsIgnoreCase(domainServiceName))
                            .findAny()
                            .ifPresent(mockDomainService -> serviceContext.setDomainService(mockDomainService));
                }
                watch.stop();
            }
            watch.start("服务封装");
            invoke.invoke(serviceContext);
            watch.stop();

            // 5. 发送异步[数据存储]事件
            watch.start("发送异步[数据存储]事件");
            invoke.handleStoreDataEvent(serviceContext);
            watch.stop();
            log.info(watch.prettyPrint(TimeUnit.SECONDS));

            // 6. 打印[结果]日志
            ServiceResultLogEvent resultLogEvent = ServiceResultLogEvent.builder()
                    .productCode(order.getProductCode())
                    .bizOrderNo(order.getBizOrderNo())
                    .build();

            // 6.1 组装返回对象
            // 6.1.1 组装无返回对象结果
            if (serviceContext.getResult() != null) {
                resultLogEvent.setResultBase(serviceContext.getResult());
            }
            // 6.1.2 组装单个返回对象
            if (serviceContext.getResult() instanceof SingleResult) {
                resultLogEvent.setSingleResult((SingleResult<?>) serviceContext.getResult());
            }
            // 6.1.3 组装集合返回对象
            if (serviceContext.getResult() instanceof ListResult) {
                resultLogEvent.setListResult((ListResult<?>) serviceContext.getResult());
            }
            // 6.1.4 组装分页返回对象
            if (serviceContext.getResult() instanceof PageResult) {
                resultLogEvent.setPageResult((PageResult<?>) serviceContext.getResult());
            }
            eventPost(resultLogEvent);
        } catch (Exception e) {
            invoke.handleException(e, serviceContext);
        }
    }

    /**
     * 领域模型的查询模式执行器
     *
     * @param serviceContext ServiceQueryContext
     * @param query          ServiceQuery
     */
    public void query(ServiceQueryContext serviceContext, ServiceQuery query) {

        // 0. 打印[开始]日志
        StopWatch watch = serviceContext.getWatch();
        watch.start("入参打印");
        ServiceOrderBase     order    = serviceContext.getOrder();
        ServiceOrderLogEvent logEvent = ServiceOrderLogEvent.builder().order(order).build();
        eventPost(logEvent);
        watch.stop();

        try {
            // 1. 校验入参
            watch.start("入参校验");
            query.checkOrder(serviceContext);
            watch.stop();

            // 2. 服务调用
            watch.start("服务调用");
            query.query(serviceContext);
            watch.stop();

            // 3. 组装返回对象
            watch.start("组装返回对象");
            query.convert(serviceContext);
            watch.stop();

            // 4. 打印[结果]日志
            ServiceResultLogEvent resultLogEvent = ServiceResultLogEvent.builder()
                    .productCode(order.getProductCode()).bizOrderNo(order.getBizOrderNo())
                    .build();

            // 4.1 组装返回对象
            // 4.1.1 组装无返回对象结果
            if (serviceContext.getResult() != null) {
                resultLogEvent.setResultBase(serviceContext.getResult());
            }
            // 4.1.2 组装单个返回对象
            if (serviceContext.getResult() instanceof SingleResult) {
                resultLogEvent.setSingleResult((SingleResult<?>) serviceContext.getResult());
            }
            // 4.1.3 组装集合返回对象
            if (serviceContext.getResult() instanceof ListResult) {
                resultLogEvent.setListResult((ListResult<?>) serviceContext.getResult());
            }
            // 4.1.4 组装分页返回对象
            if (serviceContext.getResult() instanceof PageResult) {
                resultLogEvent.setPageResult((PageResult<?>) serviceContext.getResult());
            }

            eventPost(resultLogEvent);
        } catch (Exception e) {
            query.handleException(e, serviceContext);
        }
    }

    private void eventPost(Object obj) {
        EventBus.publishAsync(obj);
        log.info("asyncEventBus post event {}", obj);
    }
}